/*
 * 
 */
package mx.unam.acatlan.poo.poligonos;


/**
 * @author Carlos Alegría Galicia
 *
 */
public class PolígonoMonótono {

	/**
	 * Los vértices del polígono
	 */
	private Punto[] vértices = null;
	
	/**
	 * El perímetro del polígono
	 */
	private float perímetro = 0;
	
	/**
	 * El área del polígono
	 */
	private float área = 0;
	
	/**
	 * Crea un {@link PolígonoMonótono} a partir de los vértices especificados.
	 * 
	 * Los vértices deben describir un polígono monótono. Un polígono monótono
	 * puede ser dividido El primer elemento
	 * debe
	 * 
	 * @param vértices
	 * @throws IllegalArgumentException 
	 */
	public PolígonoMonótono(Punto[] vértices) {
		
		//
		// verificando vértices
		//
		
		// arreglo válido
		//
		if (vértices == null) {
			throw new IllegalArgumentException(
					"El arreglo de vértices no puede ser nulo");
		}
		
		// número de vértices
		//
		if (vértices.length < 3) {
			throw new IllegalArgumentException(
					"Al menos debe haber tres vértices para formar un polígono");
		}
		
		// vértices no nulos
		//
		for (Punto punto : vértices) {
			if (punto == null) {
				throw new IllegalArgumentException(
						"Uno de los vértices es nulo");
			}
		}
		
		// monotonía
		//
		int i;
		for (i = 0; i < vértices.length - 1; i++) {
			if (vértices[i].getX() >= vértices[i + 1].getX()) break;
		}
		for (; i < vértices.length - 1; i++) {
			if (vértices[i].getX() <= vértices[i + 1].getX()) {
				throw new IllegalArgumentException(
						"Los vértices no pertenecen a un polígono monótono en "
								+ "el eje X");
			}
		}
		this.vértices = vértices;
		
		
		//
		// calculando perímetro
		//
		
		
		for (i = 0; i < vértices.length - 1; i++) {
			this.perímetro += calcularDistancia(
								this.vértices[i],
								this.vértices[i + 1]); 
		}
		this.perímetro += calcularDistancia(
							this.vértices[this.vértices.length - 1],
							this.vértices[0]);
		
		
		//
		// calculando área
		//
		
		
		// calculando la posición de la línea inferior
		//
		int líneaInferior = this.vértices[0].getY();
		for (i = 1; i < vértices.length; i++) {
			if (vértices[i].getY() < líneaInferior) {
				líneaInferior = vértices[i].getY(); 
			}
		}
		
		// cadena superior
		//
		for (i = 0; i < this.vértices.length - 1; i++) {
			if (vértices[i].getX() > vértices[i + 1].getX()) break;
			this.área += calcularÁreaTrapecio(
							this.vértices[i], this.vértices[i + 1],
							líneaInferior);
		}
		
		// cadena inferior
		//
		for (; i < vértices.length - 1; i++) {
			this.área -= calcularÁreaTrapecio(
							this.vértices[i + 1], this.vértices[i],
							líneaInferior);
		}
		this.área -= calcularÁreaTrapecio(
						this.vértices[0],
						this.vértices[this.vértices.length - 1],
						líneaInferior);
	}
	
	/**
	 * @param p1
	 * @param p2
	 * @return
	 */
	private float calcularDistancia(Punto p1, Punto p2) {
		return (float) Math.sqrt(
				Math.pow(p1.getX() - p2.getX(), 2)
				+ Math.pow(p1.getY() - p2.getY(), 2));
	}
	
	/**
	 * @param p1
	 * @param p2
	 * @return
	 */
	private float calcularÁreaTrapecio(Punto p1, Punto p2, int líneaInferior) {
		int a = p1.getY() - líneaInferior;
		int b = p2.getY() - líneaInferior;
		int h = p2.getX() - p1.getX();
		
		return 0.5f * (a + b) * h;
	}
	
	/**
	 * @return
	 */
	public Punto getPosición() {
		Punto punto = null;
		try {
			punto = (Punto) this.vértices[0].clone();
		} catch (CloneNotSupportedException e) {
		}
		return punto;
	}
	
	/**
	 * @param punto
	 */
	public void setPosición(Punto punto) {
		if (punto == null) {
			throw new IllegalArgumentException(
					"El punto utilizado como posición no puede ser nulo");
		}
		
		int dx = punto.getX() - this.vértices[0].getX();
		int dy = punto.getY() - this.vértices[0].getY();
		
		for (Punto vértice : this.vértices) {
			vértice.setX(vértice.getX() + dx);
			vértice.setY(vértice.getY() + dy);
		}
	}

	/**
	 * @return
	 */
	public float getPerímetro() {
		return perímetro;
	}

	/**
	 * @return
	 */
	public float getÁrea() {
		return área;
	}
}